package nl.tudelft.jpacman.level;

import nl.tudelft.jpacman.PacmanConfigurationException;
import nl.tudelft.jpacman.board.Board;
import nl.tudelft.jpacman.board.BoardFactory;
import nl.tudelft.jpacman.board.Square;
import nl.tudelft.jpacman.npc.Ghost;

import java.io.*;
import java.util.ArrayList;
import java.util.List;

/**
 * Parses text files into playable levels.
 */
public class MapParser {
    private final LevelFactory levelCreator;
    private final BoardFactory boardCreator;

    public MapParser(LevelFactory levelFactory, BoardFactory boardFactory) {
        this.levelCreator = levelFactory;
        this.boardCreator = boardFactory;
    }

    /**
     * Parses a 2D char array into a Level.
     */
    public Level parseMap(char[][] map) {
        int width = map.length;
        int height = map[0].length;
        Square[][] grid = new Square[width][height];
        List<Ghost> ghosts = new ArrayList<>();
        List<Square> startPositions = new ArrayList<>();

        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                char c = map[x][y];
                switch (c) {
                    case ' ': grid[x][y] = boardCreator.createGround(); break;
                    case '#': grid[x][y] = boardCreator.createWall(); break;
                    case '.':
                        Square pelletSquare = boardCreator.createGround();
                        grid[x][y] = pelletSquare;
                        levelCreator.createPellet().occupy(pelletSquare);
                        break;
                    case 'G':
                        Ghost ghost = levelCreator.createGhost();
                        ghosts.add(ghost);
                        Square ghostSquare = boardCreator.createGround();
                        ghost.occupy(ghostSquare);
                        grid[x][y] = ghostSquare;
                        break;
                    case 'P':
                        Square playerSquare = boardCreator.createGround();
                        grid[x][y] = playerSquare;
                        startPositions.add(playerSquare);
                        break;
                    default:
                        throw new PacmanConfigurationException("Invalid character: " + c);
                }
            }
        }
        return levelCreator.createLevel(boardCreator.createBoard(grid), ghosts, startPositions);
    }

    /**
     * Parses a text file into a Level.
     */
    public Level parseMap(InputStream input) throws IOException {
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(input))) {
            List<String> lines = new ArrayList<>();
            String line;
            while ((line = reader.readLine()) != null) {
                lines.add(line);
            }
            return parseMap(convertToCharArray(lines));
        }
    }

    /**
     * Converts a list of strings to a 2D char array.
     */
    private char[][] convertToCharArray(List<String> lines) {
        if (lines.isEmpty()) {
            throw new PacmanConfigurationException("Input file is empty");
        }
        int width = lines.get(0).length();
        int height = lines.size();
        char[][] map = new char[width][height];
        for (int y = 0; y < height; y++) {
            String row = lines.get(y);
            if (row.length() != width) {
                throw new PacmanConfigurationException("Map rows must have equal length");
            }
            for (int x = 0; x < width; x++) {
                map[x][y] = row.charAt(x);
            }
        }
        return map;
    }
}
